import java.util.regex.*;
import java.util.*;
import java.io.*;

/**
 * Tests voor opdracht 3.3 van TI1310 Datastructuren en Algoritmen.
 *
 * Formaat van de uitvoer van schrijfCombinaties:
 * druk EEN combinatie per regel af.
 *
 * Bijvoorbeeld:
 *
 *   [1,4]
 *   [1,2,5]
 *
 * Het precieze formaat doet er niet zo veel toe, zolang
 * - de gewichten maar los van elkaar staan,
 * - er verder geen andere cijfers bij staan, en
 * - elke combinatie op een aparte regel staat.
 *
 *
 * Gijs van Tulder, 15.02.2011
 */
public class TestOpdracht33 {
  /**
   * Runs the tests.
   */
  public static void main(String[] args) {
    runTest(new int[]{},          0,   "");
    runTest(new int[]{5},         2,   "");
    runTest(new int[]{5},         5,   "5");
    runTest(new int[]{5},         6,   "");
    
    runTest(new int[]{2,3,5},     5,   "2,3 \n 5");

    runTest(new int[]{1,2,4,8},   0,   "");
    runTest(new int[]{1,2,4,8},   1,   "1");
    runTest(new int[]{1,2,4,8},   2,   "2");
    runTest(new int[]{1,2,4,8},   3,   "1,2");
    runTest(new int[]{1,2,4,8},   4,   "4");
    runTest(new int[]{1,2,4,8},   5,   "1,4");
    runTest(new int[]{1,2,4,8},   6,   "2,4");
    runTest(new int[]{1,2,4,8},   7,   "1,2,4");
    runTest(new int[]{1,2,4,8},   8,   "8");
    runTest(new int[]{1,2,4,8},   9,   "1,8");

    runTest(new int[]{3,6,12,14,16,18},   9, "3,6");
    runTest(new int[]{3,6,12,14,16,18},  15, "3,12");
    runTest(new int[]{3,6,12,14,16,18},  18, "6,12 \n 18");
    runTest(new int[]{3,6,12,14,16,18},  30, "14,16 \n 12,18");

    runTest(new int[]{1,2,3,4,5},  6, "1,5 \n 1,2,3 \n 2,4");
    runTest(new int[]{1,2,3,4,5}, 15, "1,2,3,4,5");
  }


  /**
   * Matches groups of numbers.
   */
  private static final Pattern tokens = Pattern.compile("[0-9]+");

  /**
   * Extracts the groups of numbers from each line of str
   * and discards the non-interesting characters (whitespace etc.).
   * Returns a set of all combinations.
   */
  private static Set<String> parseString(String str) {
    Set<String> combinations = new TreeSet<String>();
    String[] lines = str.split("\n");

    // one combination per line
    for (String line : lines) {
      List<String> combination = new ArrayList<String>();
      Matcher match = tokens.matcher(line);
      
      // extract the interesting tokens (groups of numbers)
      // and add them to the set
      while (match.find()) {
        combination.add(match.group());
      }

      // if this is a real combination, add it to the collection
      if (!combination.isEmpty()) {
        Collections.sort(combination);
        combinations.add(combination.toString());
      }
    }

    return combinations;
  }

  /**
   * Checks if all items in expectedString also occur in
   * actualString. (Order insensitive.)
   * Prints the result of this test.
   */
  private static void check(String expectedString, String actualString) {
    Set<String> expected = parseString(expectedString);
    Set<String> actual   = parseString(actualString);

    if (expected.equals(actual)) {
      // exactly the same contents
      System.out.println(" success.");

    } else {
      // there is a difference
      System.out.println(" FAILED.");
      System.out.println(" Expected:  "+expected);
      System.out.println(" Actual:    "+actual);

      // expected - actual
      Set<String> missing = new TreeSet<String>(expected);
      missing.removeAll(actual);
      System.out.println(" Missing:   "+missing);

      // actual - expected
      Set<String> extra = new TreeSet<String>(actual);
      extra.removeAll(expected);
      System.out.println(" Incorrect: "+extra);

      System.out.println();
    }
  }

  /**
   * Runs a test of schrijfCombinaties with the given gewichten and gewicht.
   * Compares the result with the contents of expected.
   */
  private static void runTest(int[] gewichten, int gewicht, String expected) {
    System.out.printf("%-41s", "schrijfCombinaties("+Arrays.toString(gewichten)+", "+gewicht+");  ");

    // construct Gewichtendoos
    Gewichtendoos doos = new Gewichtendoos(gewichten.length);
    for (int g : gewichten) {
      doos.voegToe(g);
    }

    // remember the real output stream
    PrintStream outReal = System.out;

    // the output is redirected to here
    ByteArrayOutputStream capture = new ByteArrayOutputStream();

    try {
      // redirect output
      System.setOut(new PrintStream(capture));

      // execute test
      Opdracht33.schrijfCombinaties(doos, gewicht);
    }
    finally {
      // restore standard output stream
      System.setOut(outReal);
    }

    // collect output
    String output = capture.toString();

    // compare and print result
    check(expected, output);
  }
}

